#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
DMX512核心通信模块
负责DMX512协议的数据处理和串口通信
"""

import serial
import serial.tools.list_ports
import threading
import time
import numpy as np
from typing import List, Dict, Optional, Tuple


class DMXUniverse:
    """DMX512宇宙类，管理512个通道的数据"""
    
    def __init__(self):
        self.channels = np.zeros(512, dtype=np.uint8)  # 512个通道，值范围0-255
        self.dirty = False  # 标记数据是否已修改
        
    def set_channel(self, channel: int, value: int) -> bool:
        """设置单个通道值"""
        if 1 <= channel <= 512 and 0 <= value <= 255:
            if self.channels[channel - 1] != value:
                self.channels[channel - 1] = value
                self.dirty = True
            return True
        return False
    
    def get_channel(self, channel: int) -> int:
        """获取单个通道值"""
        if 1 <= channel <= 512:
            return int(self.channels[channel - 1])
        return 0
    
    def set_channels(self, start_channel: int, values: List[int]) -> bool:
        """设置多个连续通道值"""
        if start_channel < 1 or start_channel + len(values) > 513:
            return False
        
        for i, value in enumerate(values):
            if 0 <= value <= 255:
                if self.channels[start_channel - 1 + i] != value:
                    self.channels[start_channel - 1 + i] = value
                    self.dirty = True
            else:
                return False
        return True
    
    def get_channels(self, start_channel: int, count: int) -> List[int]:
        """获取多个连续通道值"""
        if start_channel < 1 or start_channel + count > 513:
            return []
        return [int(x) for x in self.channels[start_channel-1:start_channel-1+count]]
    
    def clear_all(self):
        """清零所有通道"""
        self.channels.fill(0)
        self.dirty = True
    
    def blackout(self):
        """熄灭所有灯光（通常将所有通道设为0）"""
        self.clear_all()


class DMXInterface:
    """DMX512接口抽象基类"""
    
    def __init__(self, name: str = "Unknown"):
        self.name = name
        self.is_connected = False
    
    def connect(self) -> bool:
        """连接DMX接口"""
        raise NotImplementedError
    
    def disconnect(self):
        """断开DMX接口"""
        raise NotImplementedError
    
    def send_dmx(self, universe: DMXUniverse) -> bool:
        """发送DMX数据"""
        raise NotImplementedError


class SerialDMXInterface(DMXInterface):
    """基于串口的DMX512接口"""
    
    def __init__(self, port: str, baudrate: int = 250000, name: str = "串口DMX"):
        super().__init__(name)
        self.port = port
        self.baudrate = baudrate
        self.serial_conn = None
        
    def connect(self) -> bool:
        """连接串口DMX接口"""
        try:
            self.serial_conn = serial.Serial(
                port=self.port,
                baudrate=self.baudrate,
                bytesize=serial.EIGHTBITS,
                parity=serial.PARITY_NONE,
                stopbits=serial.STOPBITS_TWO,
                timeout=1
            )
            self.is_connected = True
            return True
        except Exception as e:
            print(f"DMX串口连接失败: {e}")
            self.is_connected = False
            return False
    
    def disconnect(self):
        """断开串口连接"""
        if self.serial_conn and self.serial_conn.is_open:
            self.serial_conn.close()
        self.is_connected = False
    
    def send_dmx(self, universe: DMXUniverse) -> bool:
        """发送DMX512数据包"""
        if not self.is_connected or not self.serial_conn:
            return False
        
        try:
            # DMX512协议：
            # 1. Break (88μs低电平)
            # 2. Mark After Break (8μs高电平)
            # 3. Start Code (0x00)
            # 4. 512字节数据
            
            # 构建数据包
            dmx_packet = bytearray([0x00])  # Start Code
            dmx_packet.extend(universe.channels)  # 512个通道数据
            
            # 发送Break信号（通过改变波特率实现）
            self.serial_conn.baudrate = 88000
            self.serial_conn.write(b'\x00')
            self.serial_conn.flush()
            
            # 恢复正常波特率并发送数据
            self.serial_conn.baudrate = self.baudrate
            self.serial_conn.write(dmx_packet)
            self.serial_conn.flush()
            
            return True
        except Exception as e:
            print(f"DMX数据发送失败: {e}")
            return False


class SimulatedDMXInterface(DMXInterface):
    """模拟DMX接口（用于测试）"""
    
    def __init__(self, name: str = "模拟DMX"):
        super().__init__(name)
        self.last_data = None
        
    def connect(self) -> bool:
        """连接模拟接口"""
        self.is_connected = True
        print(f"模拟DMX接口已连接: {self.name}")
        return True
    
    def disconnect(self):
        """断开模拟接口"""
        self.is_connected = False
        print(f"模拟DMX接口已断开: {self.name}")
    
    def send_dmx(self, universe: DMXUniverse) -> bool:
        """模拟发送DMX数据"""
        if not self.is_connected:
            return False
        
        self.last_data = universe.channels.copy()
        # 打印部分通道信息用于调试
        non_zero = np.nonzero(universe.channels)[0]
        if len(non_zero) > 0:
            print(f"DMX数据已发送 - 非零通道: {non_zero[:10] + 1}...")  # 显示前10个非零通道
        return True


class DMXController:
    """DMX控制器主类"""
    
    def __init__(self):
        self.universe = DMXUniverse()
        self.interface = None
        self.auto_send = True
        self.send_interval = 1.0 / 44  # 44Hz刷新率
        self.send_thread = None
        self.running = False
        
    def set_interface(self, interface: DMXInterface) -> bool:
        """设置DMX接口"""
        if self.interface:
            self.stop_auto_send()
            self.interface.disconnect()
        
        self.interface = interface
        if interface.connect():
            if self.auto_send:
                self.start_auto_send()
            return True
        return False
    
    def start_auto_send(self):
        """开始自动发送DMX数据"""
        if not self.interface or not self.interface.is_connected:
            return False
        
        if self.send_thread and self.send_thread.is_alive():
            return True
        
        self.running = True
        self.send_thread = threading.Thread(target=self._send_loop, daemon=True)
        self.send_thread.start()
        return True
    
    def stop_auto_send(self):
        """停止自动发送"""
        self.running = False
        if self.send_thread:
            self.send_thread.join(timeout=1)
    
    def _send_loop(self):
        """发送循环线程"""
        while self.running and self.interface and self.interface.is_connected:
            if self.universe.dirty:
                self.interface.send_dmx(self.universe)
                self.universe.dirty = False
            time.sleep(self.send_interval)
    
    def send_now(self) -> bool:
        """立即发送一次DMX数据"""
        if self.interface and self.interface.is_connected:
            return self.interface.send_dmx(self.universe)
        return False
    
    def set_channel(self, channel: int, value: int) -> bool:
        """设置通道值"""
        return self.universe.set_channel(channel, value)
    
    def get_channel(self, channel: int) -> int:
        """获取通道值"""
        return self.universe.get_channel(channel)
    
    def set_channels(self, start_channel: int, values: List[int]) -> bool:
        """设置多个通道值"""
        return self.universe.set_channels(start_channel, values)
    
    def get_channels(self, start_channel: int, count: int) -> List[int]:
        """获取多个通道值"""
        return self.universe.get_channels(start_channel, count)
    
    def blackout(self):
        """熄灭所有灯光"""
        self.universe.blackout()
    
    def get_available_ports(self) -> List[str]:
        """获取可用串口列表"""
        ports = []
        for port in serial.tools.list_ports.comports():
            ports.append(port.device)
        return ports
    
    def __del__(self):
        """析构函数"""
        self.stop_auto_send()
        if self.interface:
            self.interface.disconnect()


# 设备类型定义
class DMXDevice:
    """DMX设备基类"""
    
    def __init__(self, name: str, start_channel: int, channel_count: int):
        self.name = name
        self.start_channel = start_channel
        self.channel_count = channel_count
        self.channels = {}  # 通道功能映射
        
    def get_channel_range(self) -> Tuple[int, int]:
        """获取设备占用的通道范围"""
        return (self.start_channel, self.start_channel + self.channel_count - 1)
    
    def set_parameter(self, controller: DMXController, param: str, value: int):
        """设置设备参数"""
        if param in self.channels:
            channel = self.start_channel + self.channels[param] - 1
            controller.set_channel(channel, value)


class RGBLight(DMXDevice):
    """RGB灯光设备"""
    
    def __init__(self, name: str, start_channel: int):
        super().__init__(name, start_channel, 3)
        self.channels = {
            'red': 1,
            'green': 2,
            'blue': 3
        }
    
    def set_color(self, controller: DMXController, r: int, g: int, b: int):
        """设置RGB颜色"""
        controller.set_channels(self.start_channel, [r, g, b])


class RGBWLight(DMXDevice):
    """RGBW灯光设备"""
    
    def __init__(self, name: str, start_channel: int):
        super().__init__(name, start_channel, 4)
        self.channels = {
            'red': 1,
            'green': 2,
            'blue': 3,
            'white': 4
        }
    
    def set_color(self, controller: DMXController, r: int, g: int, b: int, w: int = 0):
        """设置RGBW颜色"""
        controller.set_channels(self.start_channel, [r, g, b, w])


class MovingHead(DMXDevice):
    """摇头灯设备"""
    
    def __init__(self, name: str, start_channel: int):
        super().__init__(name, start_channel, 16)
        self.channels = {
            'pan': 1,
            'pan_fine': 2,
            'tilt': 3,
            'tilt_fine': 4,
            'dimmer': 5,
            'red': 6,
            'green': 7,
            'blue': 8,
            'white': 9,
            'color_wheel': 10,
            'gobo_wheel': 11,
            'shutter': 12,
            'zoom': 13,
            'focus': 14,
            'prism': 15,
            'control': 16
        }
    
    def set_position(self, controller: DMXController, pan: int, tilt: int):
        """设置摇头灯位置"""
        controller.set_channel(self.start_channel, pan)
        controller.set_channel(self.start_channel + 2, tilt)
    
    def set_color(self, controller: DMXController, r: int, g: int, b: int, w: int = 0):
        """设置摇头灯颜色"""
        controller.set_channel(self.start_channel + 5, r)
        controller.set_channel(self.start_channel + 6, g)
        controller.set_channel(self.start_channel + 7, b)
        controller.set_channel(self.start_channel + 8, w)


if __name__ == "__main__":
    # 测试代码
    print("DMX512核心模块测试")
    
    # 创建控制器
    controller = DMXController()
    
    # 使用模拟接口
    sim_interface = SimulatedDMXInterface("测试接口")
    controller.set_interface(sim_interface)
    
    # 创建RGB灯
    rgb_light = RGBLight("RGB灯1", 1)
    rgb_light.set_color(controller, 255, 128, 64)
    
    # 等待数据发送
    time.sleep(0.1)
    
    print("测试完成")
